#ifndef __WIZ_DATATYPE_TUPLE_ELEMENT_HPP__SHANHAOBO_19800429__
#define __WIZ_DATATYPE_TUPLE_ELEMENT_HPP__SHANHAOBO_19800429__

#include "./WizDataTypeTupleCons.hpp"

namespace Wiz
{
    namespace Tuple
    {
        namespace Element
        {
            namespace Inner
            {
                // Workaround the lack of partial specialization in some compilers
                template<::Wiz::Int::Type N>
                struct Type
                {
                    template<typename TupleT>
                    struct Inner
                    {
                    private:
                        typedef typename TupleT::tTail                                      tTail;
                        typedef Type<N - 1>                                                 tNext;

                    public:
                        typedef typename tNext::template Inner<tTail>::tReturn              tReturn;
                    };
                };
                template<>
                struct Type<0>
                {
                    template<typename TupleT>
                    struct Inner
                    {
                        typedef typename TupleT::tHead                                      tReturn;
                    };
                };
            } /// end of namespace Inner

            // Return the Nth type of the given Tuple
            template<::Wiz::Int::Type N, typename TupleT>
            struct Type
            {
            private:
                typedef ::Wiz::Tuple::Element::Inner::Type<N>                               tNth;

            public:
                typedef typename tNth::template Inner<TupleT>::tReturn                      tReturn;
                typedef tReturn                                                             tType;
            public:
                typedef typename ::Wiz::TypeTraits::Get::Reference<tType>::tType            tReference;
                typedef typename ::Wiz::TypeTraits::Get::ReferenceConst<tType>::tType       tReferenceConst;
            };

            // Return a reference to the Nth type of the given Tuple
            template<::Wiz::Int::Type N, typename TupleT>
            struct Ref
            {
            private:
                typedef typename ::Wiz::Tuple::Element::Type<N, TupleT>::tReturn            tElement;
            public:
                typedef typename ::Wiz::TypeTraits::Get::Reference<tElement>::tType         tReturn;
                typedef tReturn                                                             tType;
            };

            // Return a const reference to the Nth type of the given Tuple
            template<::Wiz::Int::Type N, typename TupleT>
            struct RefConst
            {
            private:
                typedef typename ::Wiz::Tuple::Element::Type<N, TupleT>::tReturn            tElement;
            public:
                typedef typename ::Wiz::TypeTraits::Get::ReferenceConst<tElement>::tType    tReturn;
                typedef tReturn                                                             tType;
            };
        } /// end of namespace Element

        template<::Wiz::Int::Type N>
        struct GetClass
        {
            template<typename HeadT, typename TailT>
            static WIZ_INLINE typename ::Wiz::Tuple::Element::Ref<N, ::Wiz::Tuple::Cons::Type<HeadT, TailT>>::tReturn
            Get(::Wiz::Tuple::Cons::Type<HeadT, TailT>& t)
            {
                return GetClass<N - 1>::Get(t.m_Tail);
            }

            template<typename HeadT, typename TailT>
            static WIZ_INLINE typename ::Wiz::Tuple::Element::RefConst<N, ::Wiz::Tuple::Cons::Type<HeadT, TailT>>::tReturn
            Get(const ::Wiz::Tuple::Cons::Type<HeadT, TailT>& t)
            {
                return GetClass<N - 1>::Get(t.m_Tail);
            }
        };

        template<>
        struct GetClass<0>
        {
            template<typename HeadT, typename TailT>
            static WIZ_INLINE typename ::Wiz::TypeTraits::Get::Reference<HeadT>::tType
            Get(::Wiz::Tuple::Cons::Type<HeadT, TailT>& t)
            {
                return t.m_Head;
            }

            template<typename HeadT, typename TailT>
            static WIZ_INLINE typename ::Wiz::TypeTraits::Get::ReferenceConst<HeadT>::tType
            Get(const ::Wiz::Tuple::Cons::Type<HeadT, TailT>& t)
            {
                return t.m_Head;
            }
        };

        template<::Wiz::Int::Type N, typename HeadT, typename TailT>
        typename ::Wiz::Tuple::Element::Ref<N, ::Wiz::Tuple::Cons::Type<HeadT, TailT>>::tReturn
        Get(::Wiz::Tuple::Cons::Type<HeadT, TailT>& t)
        {
            return ::Wiz::Tuple::GetClass<N>::Get(t);
        }

        template<::Wiz::Int::Type N, typename HeadT, typename TailT>
        typename ::Wiz::Tuple::Element::RefConst<N, ::Wiz::Tuple::Cons::Type<HeadT, TailT>>::tReturn
        Get(const ::Wiz::Tuple::Cons::Type<HeadT, TailT>& t)
        {
            return ::Wiz::Tuple::GetClass<N>::Get(t);
        }
    } /// end of namespace Tuple
} /// end of namespace Wiz

#endif /*__WIZ_DATATYPE_TUPLE_ELEMENT_HPP__SHANHAOBO_19800429__*/
